(function(){
	var win = window;
	var doc = document;
	var body = doc.body;
	var currentPage;
	var backbutton = doc.getElementById('backbutton');
	var title = doc.getElementById('title');
	var topbar = doc.getElementById('topbar');
	var firstPage = body.querySelector('.page');
	var pageHistory = [];
	var timerId;
	var isAndroid = (/android/gi).test(navigator.appVersion);
	var scrollTop = 0;
	var scrollLeft = 0;
	var inputSelector = "input[type='text'], input[type='search'], input[type='url'], input[type='email'], input[type='tel'], input[type='number'], input[type='password'], input[type='datetime'], input[type='datetime-local'], input[type='date'], input[type='month'], input[type='week'], input[type='time']";
	
	if (win.location.hash.length > 0) {
		var href = win.location.href;
		var hashIndex = href.indexOf('#');
		win.location.href = href.substr(0, hashIndex);
	}
	function floor (r) {
		return r >> 0;
	}
	
	Scroller.prototype = {
		handleEvent: function(e){
			switch(e.type) {
				case 'touchstart':
					this.ontouchstart(e);
					break;
				case 'touchmove':
					this.ontouchmove(e);
					break;
				case 'touchend':
					this.ontouchend(e);
					break;
				case 'webkitTransitionEnd':
					this.onscrollend();
					break;
				case 'orientationchange':
					this.onorientationchange(e);
					break;
			}
		},
		refresh: function() {
			var win = window;
			var wrapper = this.wrapper;
			var scroller = this.scroller;
			
			if(wrapper.offsetLeft + wrapper.clientWidth <= win.innerWidth){
				this.wrapperW = floor(wrapper.clientWidth);
			} else {
				this.wrapperW = floor(win.innerWidth - wrapper.offsetLeft);
			}
			if(wrapper.offsetTop + wrapper.clientHeight <= win.innerHeight){
				this.wrapperH = floor(wrapper.clientHeight);
			} else {
				this.wrapperH = floor(win.innerHeight - wrapper.offsetTop);
			}
			this.scrollerW = floor(scroller.scrollWidth);			
			this.scrollerH = floor(scroller.scrollHeight);
			this.maxScrollX = floor(this.wrapperW - this.scrollerW);
			this.maxScrollX = this.maxScrollX >= 0 ? 0 : this.maxScrollX;
			this.maxScrollY = floor(this.wrapperH - this.scrollerH + 2);
			this.maxScrollY = this.maxScrollY >= 0 ? 0 : this.maxScrollY;
			
			this.startX = 0;
			this.startY = 0;
		},
		ontouchstart: function(e) {
			var target = e.target;
			if (e.touches.length > 1) {
				e.preventDefault();
				return;
			}
			if (target === this.scroller) {
				e.preventDefault();					
			}
			clearTimeout(timerId);
			var t = e.touches[0];
			this.startX = t.clientX - this.currentX;
			this.startClientX = t.clientX;
			this.startY = t.clientY - this.currentY;
			this.startClientY = t.clientY;
			this.moved = false;
			this.canceled = false;
			
			this.wrapper.addEventListener('touchmove', this);
			this.wrapper.addEventListener('touchend', this);
		},
		ontouchmove: function(e) {
			e.preventDefault();
			var t = e.touches[0];
			var diffX = Math.abs(t.clientX - this.startClientX);
			var diffY = Math.abs(t.clientY - this.startClientY);
			if (this.moved) {
				e.stopPropagation();
			} else if (this.canceled) {
				return;
			} else if (this.options.vscroll && diffY <= 6 
				|| this.options.hscroll && diffX <= 6) {
				return;
			} else if (this.options.vscroll && diffY >= diffX
				|| this.options.hscroll && diffX >= diffY) {
					e.stopPropagation();
			} else {
				this.canceled = true;
				return;
			}
			if (!this.moved) {
				this.moved = true;
				if (this.options.onscrollstart) {
					this.options.onscrollstart.call(this, this);
				}
			}
			var dx = 0;
			if (this.options.hscroll) {
				this.previousX = this.currentX;
				this.currentX = t.clientX - this.startX;
				dx = this.currentX;
				if (dx) {
					if (dx > 0) {
						dx = dx/2;
					} else if (dx < this.maxScrollX) {
						dx = (dx + this.maxScrollX)/2;
					}
				}
			}		

			var dy = 0;
			if (this.options.vscroll) {
				this.previousY = this.currentY;
				this.currentY = t.clientY - this.startY;
				dy = this.currentY;
				if (dy) {
					if (dy > 0) {
						dy = dy/2;
					} else if (dy < this.maxScrollY) {
						dy = (dy + this.maxScrollY)/2;
					}
				}
			}
			this.pos(dx, dy, 0);
			if (this.options.onscrollmove) {
				this.options.onscrollmove.call(this, this);
			}
		},
		ontouchend: function(e) {
			if (e.touches.length > 0) {
				return;
			}
			var that = this;
			that.wrapper.removeEventListener('touchmove', that);
			that.wrapper.removeEventListener('touchend', that);
			
			if (that.moved) {
				if (that.options.hscroll){
					that._scroll('X');
				} else if (that.options.vscroll) {
					that._scroll('Y');
				}
			} 
		},
		onscrollend: function() {
			this.currentX = this.targetCurrentX;
			this.currentY = this.targetCurrentY;
			this.scroller.removeEventListener('webkitTransitionEnd', this);
			if (this.timerId) {
				clearInterval(this.timerId);
				this.timerId = null;
			}
			this.options.onscrollend.call(this, this);
		},
		onorientationchange: function(e) {
			if (this.wrapper.style.display === 'block' || this.wrapper.style.display === '') {
				var that = this;
					setTimeout(function() {
						that.refresh();
					}, 200);
			}
		},
		scrollTo: function(x, y, time) {
			this.currentX = x;
			this.currentY = y;
			this.pos(x, y, time);
		},
		pos: function(x, y, time) {
			this.scroller.style.webkitTransitionDuration = time + 'ms';
			this.scroller.style.webkitTransform = 'translate3d(' + x + 'px, ' + y + 'px, 0)';			
		},
		_scroll: function(dir) {
			var that = this;
			var currentPosName = 'current' + dir;
			var targetCurrentPosName = 'targetCurrent' + dir;
			var previousPosName = 'previous' + dir;
			var maxScrollPosName = 'maxScroll' + dir;
			var mName = 'm' + (dir === 'X' ? '41' : '42');
			var d = (that[currentPosName] - that[previousPosName]) * Math.abs(that[currentPosName] - that[previousPosName]) + that[currentPosName];
			var tranend;
			that.targetCurrentX = 0;
			that.targetCurrentY = 0;
			var current = that.options.onscrollmove || that.options.onscrollend ? new WebKitCSSMatrix(window.getComputedStyle(that.scroller, null).webkitTransform)[mName] : undefined;
			if (d) {
				if (that.options.onscrollmove) {
					that.timerId = setInterval(function() {
						if (that.timerId) {
							that[currentPosName] = new WebKitCSSMatrix(window.getComputedStyle(that.scroller, null).webkitTransform)[mName];
							that.options.onscrollmove.call(that, that);
						}
					}, 20);
				}
				if (d < that[maxScrollPosName]) {
					if (that[previousPosName] > that[maxScrollPosName]) {
						d = that[maxScrollPosName];
						that[currentPosName] = d;
						var time = Math.abs(that[currentPosName] - that[previousPosName]);
						tranend = function(e){
							that.scroller.removeEventListener('webkitTransitionEnd', tranend);
							that[targetCurrentPosName] = d;
							that._addTransitionEndEvent();
							_pos(that, d, 0, 300, dir);
						};
						that.scroller.addEventListener('webkitTransitionEnd', tranend);
						_pos(that, d - 40, 0, time, dir);
					} else {
						d = that[maxScrollPosName];
						that[targetCurrentPosName] = d;
						if (current === d) {
							if (that.options.onscrollmove || that.options.onscrollend) {
								that.onscrollend();
							}
						} else {
							that._addTransitionEndEvent();
							_scrollTo(that, d, 0, 500, dir);
						}
					}
				} else if (d > 0) {
					if (that[previousPosName] < 0) {
						d = 0;
						that[currentPosName] = 0;
						var time = Math.abs(that[currentPosName] - that[previousPosName]);
						tranend = function(e){
							that.scroller.removeEventListener('webkitTransitionEnd', tranend);
							that[targetCurrentPosName] = d;
							that._addTransitionEndEvent();
							_pos(that, d, 0, 300, dir);
						};
						that.scroller.addEventListener('webkitTransitionEnd', tranend);
						_pos(that, d + 40, 0, time, dir);
					} else {
						that[targetCurrentPosName] = 0;
						if (current === 0) {
							if (that.options.onscrollmove || that.options.onscrollend) {
								that.onscrollend();
							}
						} else {
							that._addTransitionEndEvent();
							that.scrollTo(0, 0, 500);
						}
					}
				} else {
					if (that.options.snap) {
						if (d < that[maxScrollPosName] / 2) {
							that[targetCurrentPosName] = that[maxScrollPosName];
							if (current === that[maxScrollPosName]) {
								if (that.options.onscrollmove || that.options.onscrollend) {
									that.onscrollend();
								}
							} else {
								that._addTransitionEndEvent();
								_scrollTo(that, that[maxScrollPosName], 0, 500, dir);
							}
						} else {
							that[targetCurrentPosName] = 0;
							if (current === 0) {
								if (that.options.onscrollmove || that.options.onscrollend) {
									that.onscrollend();
								}
							} else {
								that._addTransitionEndEvent();
								that.scrollTo(0, 0, 500);
							}
						}
					} else {
						that[targetCurrentPosName] = d;
						if (current === d) {
							if (that.options.onscrollmove || that.options.onscrollend) {
								that.onscrollend();
							}
						} else {
							that._addTransitionEndEvent();
							_scrollTo(that, d, 0, 2000, dir);
						}
					}
				}
			} else {
				if (that.options.onscrollmove || that.options.onscrollend) {
					that.onscrollend();
				}
			}
			
			function _pos(scroller, x, y, time, dir) {
				if (dir === 'X') {
					scroller.pos(x, y, time);
				} else {
					scroller.pos(y, x, time);
				}
			}
			function _scrollTo(scroller, x, y, time, dir) {
				if (dir === 'X') {
					scroller.scrollTo(x, y, time);
				} else {
					scroller.scrollTo(y, x, time);
				}
			}
		},
		_addTransitionEndEvent: function() {
			if (this.options.onscrollmove || this.options.onscrollend) {
				this.scroller.addEventListener('webkitTransitionEnd', this);
			}
		}
	};
	if (firstPage) {
		showPage(firstPage, false);
	}

	if (backbutton) {
		backbutton.addEventListener('click', function() {
			win.location.hash = '#' + pageHistory[pageHistory.length - 1].id.substr(1);
		});
	}

	if(body.querySelector('.scroll')){
		body.addEventListener('touchmove', function(e) {
			e.preventDefault();
		});
	}
	
	win.addEventListener('hashchange', function(e) {
		var hash = win.location.hash;
		var page;
		if (hash.length === 0) {
			page = firstPage;
		} else {
			hash = hash.substr(1);
			page = document.getElementById('_'+hash);
		}
		if (page) {
			showPage(page, processBackwards(page));
		}
	});
	function showPage(page, backwards) {
		setupTitle(page);
		setupBackbutton();
		
		if (!currentPage) {
			prepareFirstPage(page);
			return;
		}
		var windowWidth = win.innerWidth + 'px';
		prepareNextPage(page, windowWidth);
		
		setTimeout(function() {
			doPageTransition(page, backwards, windowWidth);

			setTimeout(function() {
				endPageTransiton(page, backwards);
			}, 300);

		}, 0);
		
		function setupTitle(page) {
			if (title) {
				title.innerHTML = page.getAttribute('data-title');
			}
		}
		
		function loadPage(page) {
			page.style.display = 'block';
			setScrollerIfNeed(page);
			if (page.scroller) {
				window.addEventListener('orientationchange', page.scroller);
			}
			var textfields = page.querySelectorAll(inputSelector);
			for (var i = textfields.length; i--; ) {
				var textfield = textfields[i]; 
				textfield.addEventListener('focus', oninputfocus);
				textfield.addEventListener('blur', oninputblur);
			}
		}
		
		function oninputfocus(e) {
			scrollTop = body.scrollTop;
			scrollLeft = body.scrollLeft;
		}

		function oninputblur(e) {
			if (scrollLeft !== 0) {
				window.scrollTo(0, scrollTop);
			}
		}
				
		function prepareFirstPage(page) {
			loadPage(page);
			currentPage = page;
			if (page.scroller) {
				setTimeout(function(){
					page.scroller.refresh();
				}, 0);
			}
		}
		
		function prepareNextPage(page, windowWidth) {
			page.style.webkitTransitionDuration = '0ms';
			page.style.webkitTransform = 'translate3d(' + (backwards ? '-' : '') + windowWidth + ', 0, 0)';
			loadPage(page);
		}
		
		function doPageTransition(page, backwards, windowWidth) {
			currentPage.style.webkitTransitionDuration = '300ms';
			currentPage.style.webkitTransform = 'translate3d(' + (backwards ? '' : '-') + windowWidth + ', 0, 0)';
			if (page.scroller) {
				page.scroller.refresh();
			}
			page.style.webkitTransitionDuration = '300ms';
			page.style.webkitTransform = 'translate3d(0, 0, 0)';
		}
		
		function endPageTransiton(page, backwards) {
			unloadPage(page, backwards);
		}
		
		function setupBackbutton() {
			if (backbutton) {
				var length = pageHistory.length;
				if (length > 0) {
					backbutton.style.display = 'block';
					var page2 = backwards ? pageHistory[length - 1] : currentPage;
					var backTitle = page2.getAttribute('data-title');
					if (!backTitle) {
						backTitle = 'Back';
					}
					backbutton.innerHTML = backTitle;
				} else {
					backbutton.style.display = 'none';
				}
			}
		}
		
		function unloadPage(page, backwards) {
			var scroller = currentPage.scroller;
			if (scroller) {
				if (backwards) {
					scroller.scrollTo(0, 0);
				}
				window.removeEventListener('orientationchange', scroller);
			}
			var textfields = currentPage.querySelectorAll(inputSelector);
			for (var i = textfields.length; i--; ) {
				var textfield = textfields[i]; 
				textfield.removeEventListener('focus', oninputfocus);
				textfield.removeEventListener('blur', oninputblur);
			}
			currentPage.style.display = 'none';
			currentPage = page;
		}
	}

	function processBackwards(page) {
		var count = pageHistory.length;
		for (var i = count - 1; i >= 0; i--) {
			if (pageHistory[i] === page) {
				pageHistory.splice(i);
				return true;
			}
		}
		pageHistory.push(currentPage);
		return false;
	}

	function Scroller(el, options) {
		this.wrapper = typeof el == 'string' ? doc.getElementById(el) : el;
		this.options = {
			vscroll : true,
			hscroll : false,
			snap : false,
			onscrollstart: null,
			onscrollmove: null,
			onscrollend: null,
		};
		for (var o in options) {
			this.options[o] = options[o];
		}
		options = this.options;
		this.scroller = this.wrapper.firstElementChild;

		this.currentX = 0;
		this.currentY = 0;
		this.previousX = 0;
		this.previousY = 0;

		this.wrapper.addEventListener('touchstart', this);
	}

	function setScrollerIfNeed(page) {
		if (page.scrollerDone) {
			return;
		}
		if((/scroll/gi).test(page.className)){
			_setScrollerIfNeed(page);
		}
		iterateScrollable(page);
		page.scrollerDone = true;
	}
	
	function iterateScrollable(page) {
		var scrollables = page.querySelectorAll('.scroll');
		if (scrollables) {
			var size = scrollables.length;
			for (var i = 0; i < size; i++) {
				_setScrollerIfNeed(scrollables[i]);
			}
		}
	}

	function _setScrollerIfNeed(el) {
		if(el.scroller){
			return;
		}
		var doc = document;
		var scroll = el.getAttribute('data-scroll');
		var hscroll = scroll === 'h';
		var vscroll = !hscroll;
		if (vscroll || hscroll) {
			var scroller = doc.createElement('div');
			scroller.style.position = 'relative';
			scroller.style.width = '100%';
			scroller.style.webkitTransitionProperty = '-webkit-transform';
			scroller.style['-webkit-transition-timing-function'] = 'cubic-bezier(0,0,0.25,1)';
			scroller.style.padding = '1px 0';
			scroller.style.margin = '-1px 0';
			
			var range = doc.createRange();
			range.selectNodeContents(el);
			range.surroundContents(scroller);
			scroller.style.webkitTransform = 'translate3d(0, 0, 0)';
			
			var snap = Boolean(el.getAttribute('data-snap'));
			var options = {
				"vscroll" : vscroll,
				"hscroll" : hscroll,
				"snap" : snap,
			};
			
			el.scroller = new Scroller(el, options);
			if (!(/page/gi).test(el.className)) {
				el.scroller.refresh();
			}
		}
	}
}());